﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Microsoft.Practices.CompositeUI.EventBroker
{
    /// <summary>
    /// 	Maintains the subscriptions registered from a <see cref = "WorkItem" />
    /// </summary>
    internal class WorkItemSubscriptions : IEnumerable<Subscription>
    {
        #region Fields & Properties

        private readonly WeakReference _weakWorkItem;

        /// <summary>
        /// </summary>
        public WorkItem WorkItem
        {
            get
            {
                return (WorkItem) _weakWorkItem.Target;
            }
        }

        /// <summary>
        /// </summary>
        public int SubscriptionCount
        {
            get
            {
                lock (_gate)
                {
                    return _subscriptions.Count;
                }
            }
        }

        #endregion

        #region Collection Fields & Properties

        private readonly List<Subscription> _subscriptions = new List<Subscription>();

        #endregion

        #region Lock Object

        private readonly object _gate = new object();

        #endregion

        #region Constructors

        public WorkItemSubscriptions(WorkItem workItem)
        {
            _weakWorkItem = new WeakReference(workItem);
        }

        #endregion

        #region Implementation of IEnumerable

        /// <summary>
        /// 	Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// 	A <see cref = "T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>1</filterpriority>
        public IEnumerator<Subscription> GetEnumerator()
        {
            return _subscriptions.GetEnumerator();
        }

        /// <summary>
        /// 	Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// 	An <see cref = "T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        #region Add Subscription

        public void AddSubscription(object subscriber, string handlerMethodName, ThreadOption threadOption)
        {
            AddSubscription(subscriber, handlerMethodName, null, threadOption);
        }

        public void AddSubscription(object subscriber, string handlerMethodName, Type[] parameterTypes, ThreadOption threadOption)
        {
            lock (_gate)
            {
                var subscription = FindSubscription(subscriber, handlerMethodName);
                if (subscription == null)
                {
                    _subscriptions.Add(new Subscription(this, subscriber, handlerMethodName, parameterTypes, threadOption));
                }
            }
        }

        #endregion

        #region Remove Subscription

        public void RemoveSubscription(Subscription subscription)
        {
            lock (_gate)
            {
                _subscriptions.Remove(subscription);
            }
        }

        public void RemoveSubscription(object subscriber, string handlerMethodName)
        {
            lock (_gate)
            {
                var subscription = FindSubscription(subscriber, handlerMethodName);
                if (subscription != null)
                {
                    RemoveSubscription(subscription);
                }
            }
        }

        #endregion

        #region Find Subscription

        public Subscription FindSubscription(object subscriber, string handlerMethodName)
        {
            lock (_gate)
            {
                Clean();
                return _subscriptions.FirstOrDefault(subscription => subscription.Subscriber == subscriber && subscription.HandlerMethodName == handlerMethodName);
            }
        }

        #endregion

        #region Get Handlers

        public Action<object, EventArgs, List<Exception>, TraceSource>[] GetHandlers()
        {
            lock (_gate)
            {
                return _subscriptions.Select(subscription => subscription.Handler).Where(handler => handler != null).ToArray();
            }
        }

        #endregion

        #region Clean

        public void Clean()
        {
            lock (_gate)
            {
                if (WorkItem == null || (WorkItem != null && WorkItem.Status == WorkItemStatus.Terminated))
                {
                    _subscriptions.Clear();
                }
                else
                {
                    foreach (var subscription in _subscriptions.ToArray().Where(subscription => subscription.Subscriber == null))
                    {
                        RemoveSubscription(subscription);
                    }
                }
            }
        }

        #endregion
    }
}